﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Controls;
using System.Collections;
using System.Windows.Documents;
using System.Reflection;

namespace FarpacHelper
{
    public class DragDropHelper
    {
        // source and target
        private DataFormat format = DataFormats.GetDataFormat("DragDropItemsControl");
        private Point initialMousePosition;
        private Vector initialMouseOffset;
        private Tool draggedData;
        private DraggedAdorner draggedAdorner;
        private InsertionAdorner insertionAdorner;
        private Window topWindow;
        // source
        private ItemsControl sourceItemsControl;
        private FrameworkElement sourceItemContainer;
        // target
        private ItemsControl targetItemsControl;
        private FrameworkElement targetItemContainer;
        private bool hasVerticalOrientation;
        private int insertionIndex;
        private bool isInFirstHalf;
        // singleton
        private static DragDropHelper instance;
        private static DragDropHelper Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new DragDropHelper();
                }
                return instance;
            }
        }

        #region IsDragSourceProperty
        public static readonly DependencyProperty IsDragSourceProperty =
            DependencyProperty.RegisterAttached("IsDragSource", typeof(bool), typeof(DragDropHelper), new UIPropertyMetadata(false, IsDragSourceChanged));

        public static bool GetIsDragSource(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsDragSourceProperty);
        }

        public static void SetIsDragSource(DependencyObject obj, bool value)
        {
            obj.SetValue(IsDragSourceProperty, value);
        }

        private static void IsDragSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var dragSource = obj as ItemsControl;
            if (dragSource != null)
            {
                if (Object.Equals(e.NewValue, true))
                {
                    dragSource.PreviewMouseLeftButtonDown += Instance.DragSource_PreviewMouseLeftButtonDown;
                    dragSource.PreviewMouseLeftButtonUp += Instance.DragSource_PreviewMouseLeftButtonUp;
                    dragSource.PreviewMouseMove += Instance.DragSource_PreviewMouseMove;
                }
                else
                {
                    dragSource.PreviewMouseLeftButtonDown -= Instance.DragSource_PreviewMouseLeftButtonDown;
                    dragSource.PreviewMouseLeftButtonUp -= Instance.DragSource_PreviewMouseLeftButtonUp;
                    dragSource.PreviewMouseMove -= Instance.DragSource_PreviewMouseMove;
                }
            }
        }
        #endregion

        #region IsDropTargetProperty
        public static readonly DependencyProperty IsDropTargetProperty =
            DependencyProperty.RegisterAttached("IsDropTarget", typeof(bool), typeof(DragDropHelper), new UIPropertyMetadata(false, IsDropTargetChanged));

        public static bool GetIsDropTarget(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsDropTargetProperty);
        }

        public static void SetIsDropTarget(DependencyObject obj, bool value)
        {
            obj.SetValue(IsDropTargetProperty, value);
        }

        private static void IsDropTargetChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var dropTarget = obj as ItemsControl;
            if (dropTarget != null)
            {
                if (Object.Equals(e.NewValue, true))
                {
                    dropTarget.AllowDrop = true;
                    dropTarget.PreviewDrop += Instance.DropTarget_PreviewDrop;
                    dropTarget.PreviewDragEnter += Instance.DropTarget_PreviewDragEnter;
                    dropTarget.PreviewDragOver += Instance.DropTarget_PreviewDragOver;
                    dropTarget.PreviewDragLeave += Instance.DropTarget_PreviewDragLeave;
                }
                else
                {
                    dropTarget.AllowDrop = false;
                    dropTarget.PreviewDrop -= Instance.DropTarget_PreviewDrop;
                    dropTarget.PreviewDragEnter -= Instance.DropTarget_PreviewDragEnter;
                    dropTarget.PreviewDragOver -= Instance.DropTarget_PreviewDragOver;
                    dropTarget.PreviewDragLeave -= Instance.DropTarget_PreviewDragLeave;
                }
            }
        }
        #endregion

        #region DragDropTemplateProperty
        public static DataTemplate GetDragDropTemplate(DependencyObject obj)
        {
            return (DataTemplate)obj.GetValue(DragDropTemplateProperty);
        }

        public static void SetDragDropTemplate(DependencyObject obj, DataTemplate value)
        {
            obj.SetValue(DragDropTemplateProperty, value);
        }

        public static readonly DependencyProperty DragDropTemplateProperty =
            DependencyProperty.RegisterAttached("DragDropTemplate", typeof(DataTemplate), typeof(DragDropHelper), new UIPropertyMetadata(null));
        #endregion

        #region IsCopyProperty
        public static readonly DependencyProperty IsCopyProperty =
            DependencyProperty.RegisterAttached("IsCopy", typeof(bool), typeof(DragDropHelper), new UIPropertyMetadata(false));

        public static bool GetIsCopy(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsCopyProperty);
        }

        public static void SetIsCopy(DependencyObject obj, bool value)
        {
            obj.SetValue(IsCopyProperty, value);
        }
        #endregion

        #region DoDragDropProperty
        public static readonly DependencyProperty DoDragDropProperty =
            DependencyProperty.RegisterAttached("DoDragDrop", typeof(DoDragDropTargetEventArgs), typeof(DragDropHelper), new UIPropertyMetadata(null, DoDragDropChanged));

        public static DoDragDropTargetEventArgs GetDoDragDrop(DependencyObject obj)
        {
            return (DoDragDropTargetEventArgs)obj.GetValue(DoDragDropProperty);
        }

        public static void SetDoDragDrop(DependencyObject obj, DoDragDropTargetEventArgs value)
        {
            obj.SetValue(DoDragDropProperty, value);
        }

        private static void DoDragDropChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var TargetArg = e.NewValue as DoDragDropTargetEventArgs;
            if (TargetArg != null)
            {   // If change to not NULL execute METHOD
                var dragSource = obj as ItemsControl;
                var dropTarget = TargetArg.Target;
                object draggedItem = TargetArg.Item;
                int insertIndex = TargetArg.Index;
                if (insertIndex == -1) insertIndex = dropTarget.Items.Count; // If -1 then add to end of items list.
                // MORE CODE HERE

                // If Copy no need to Removed
                int indexRemoved = -1; bool diff = false;

                if (draggedItem != null)
                {
                    // If DragSource IsCopy Mode => indexRemoved (Else indexRemoved = -1)
                    if (GetIsCopy(dragSource) == false) indexRemoved = Utilities.RemoveItemFromItemsControl(dragSource, draggedItem);

                    if (dragSource == dropTarget)
                    {   // This happens when we drag an item to a later position within the same ItemsControl.
                        if (indexRemoved != -1 && indexRemoved < insertIndex)
                        {
                            insertIndex--;
                        }
                    }
                    else
                    {   // This happen when we drag an item to different control
                        diff = true;
                    }

                    // If DragSource IsCopy Mode => Clone when Insert Different Control
                    if (GetIsCopy(dragSource) == true)
                    {
                        if (diff) Utilities.InsertItemInItemsControl(dropTarget, (draggedItem as Tool).Clone(), insertIndex);
                        else Utilities.InsertItemInItemsControl(dropTarget, draggedItem, insertIndex); // Same Control => Move only (NEED TEST HERE ! NO USE)
                    }
                    else Utilities.InsertItemInItemsControl(dropTarget, draggedItem, insertIndex); // Same Control => Move only (NEED TEST HERE ! NO USE)
                    //Instance.RemoveDraggedAdorner();
                    //Instance.RemoveInsertionAdorner();
                }


                if (diff)
                {   // If different control then :
                    ICommand command = GetPreviewDropFromDiffHandler(dropTarget);
                    if (command != null) command.Execute(draggedItem); // PreviewDropToDiffHandler Callback (Wrapper by ICommand)

                    command = GetPreviewDropToDiffHandler(dragSource);
                    if (command != null) command.Execute(draggedItem); // PreviewDropToDiffHandler Callback (Wrapper by ICommand)
                }

            }
            // Code to reset NewValue to avoid not call DoDragDropChanged if same DoDragDropTargetEventArgs pass into HERE
        }
        #endregion

        #region DoRemoveItemProperty
        public static readonly DependencyProperty DoRemoveItemProperty =
            DependencyProperty.RegisterAttached("DoRemoveItem", typeof(DoRemoveItemArgs), typeof(DragDropHelper), new UIPropertyMetadata(null, DoRemoveItemChanged));

        public static DoRemoveItemArgs GetDoRemoveItem(DependencyObject obj)
        {
            return (DoRemoveItemArgs)obj.GetValue(DoRemoveItemProperty);
        }

        public static void SetDoRemoveItem(DependencyObject obj, DoRemoveItemArgs value)
        {
            obj.SetValue(DoRemoveItemProperty, value);
        }

        private static void DoRemoveItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var removeItem = (e.NewValue as DoRemoveItemArgs).Item;
            if (removeItem != null)
            {   // If change to not NULL execute METHOD
                var removeSource = obj as ItemsControl;
                Utilities.RemoveItemFromItemsControl(removeSource, removeItem);

                // Different control but don't have dropTarget then :
                ICommand command = GetPreviewDropToDiffHandler(removeSource);
                if (command != null) command.Execute(removeItem); // PreviewDropToDiffHandler Callback (Wrapper by ICommand)
            }
            // Code to reset NewValue to avoid not call DoDragDropChanged if same DoDragDropTargetEventArgs pass into HERE
        }
        #endregion

        #region PreviewDropFromDiffCommand
        /// <summary>
        /// Get the PreviewDropHandler for this Target Object (own this Property) from Different Source obj. If obj = null then no need to Call this
        /// </summary>
        /// <param name="obj">ItemsControl Source that Different from this Target Object (own this Property)</param>
        /// <returns></returns>
        public static ICommand GetPreviewDropFromDiffHandler(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(PreviewDropFromDiffHandlerProperty);
        }

        public static void SetPreviewDropFromDiffHandler(DependencyObject obj, ICommand value)
        {
            obj.SetValue(PreviewDropFromDiffHandlerProperty, value);
        }

        public static readonly DependencyProperty PreviewDropFromDiffHandlerProperty =
            DependencyProperty.RegisterAttached("PreviewDropFromDiffHandler", typeof(ICommand), typeof(DragDropHelper), new UIPropertyMetadata(null));
        #endregion

        #region PreviewDropToDiffCommand
        public static ICommand GetPreviewDropToDiffHandler(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(PreviewDropToDiffHandlerProperty);
        }

        public static void SetPreviewDropToDiffHandler(DependencyObject obj, ICommand value)
        {
            obj.SetValue(PreviewDropToDiffHandlerProperty, value);
        }

        public static readonly DependencyProperty PreviewDropToDiffHandlerProperty =
            DependencyProperty.RegisterAttached("PreviewDropToDiffHandler", typeof(ICommand), typeof(DragDropHelper), new UIPropertyMetadata(null));
        #endregion

        // DragSource

        private void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.sourceItemsControl = (ItemsControl)sender;
            Visual visual = e.OriginalSource as Visual;

            this.topWindow = Window.GetWindow(this.sourceItemsControl);
            this.initialMousePosition = e.GetPosition(this.topWindow);

            this.sourceItemContainer = sourceItemsControl.ContainerFromElement(visual) as FrameworkElement;
            if (this.sourceItemContainer != null)
            {
                this.draggedData = this.sourceItemContainer.DataContext as Tool;
            }
        }

        // Drag = mouse down + move by a certain amount
        private void DragSource_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (this.draggedData != null)
            {
                // Only drag when user moved the mouse by a reasonable amount.
                if (Utilities.IsMovementBigEnough(this.initialMousePosition, e.GetPosition(this.topWindow)))
                {
                    this.initialMouseOffset = this.initialMousePosition - this.sourceItemContainer.TranslatePoint(new Point(0, 0), this.topWindow);
                    //DataObject data = new DataObject(this.format.Name, this.draggedData);
                    // Adding events to the window to make sure dragged adorner comes up when mouse is not over a drop target.
                    bool previousAllowDrop = this.topWindow.AllowDrop;
                    this.topWindow.AllowDrop = true;
                    this.topWindow.DragEnter += TopWindow_DragEnter;
                    this.topWindow.DragOver += TopWindow_DragOver;
                    this.topWindow.DragLeave += TopWindow_DragLeave;

                    // If source isCopy then
                    //      If have target 
                    //          If same source Move
                    //          If diff source Copy
                    //      If no target
                    //          Copy
                    // Else Move

                    // If source isCopy & have target & diff target then Copy
                    // Else Move
                    if (GetIsCopy(this.sourceItemsControl))
                    {
                        if (this.targetItemsControl == null) DragDrop.DoDragDrop((DependencyObject)sender, new DataObject(this.format.Name, this.draggedData.Clone()), DragDropEffects.Copy);
                        else
                        {
                            if (this.sourceItemsControl != this.targetItemsControl) DragDrop.DoDragDrop((DependencyObject)sender, new DataObject(this.format.Name, this.draggedData.Clone()), DragDropEffects.Copy);
                            else DragDrop.DoDragDrop((DependencyObject)sender, new DataObject(this.format.Name, this.draggedData), DragDropEffects.Move);
                        }
                    }
                    else DragDrop.DoDragDrop((DependencyObject)sender, new DataObject(this.format.Name, this.draggedData), DragDropEffects.Move);

                    // Without this call, there would be a bug in the following scenario: Click on a data item, and drag
                    // the mouse very fast outside of the window. When doing this really fast, for some reason I don't get 
                    // the Window leave event, and the dragged adorner is left behind.
                    // With this call, the dragged adorner will disappear when we release the mouse outside of the window,
                    // which is when the DoDragDrop synchronous method returns.
                    RemoveDraggedAdorner();

                    this.topWindow.AllowDrop = previousAllowDrop;
                    this.topWindow.DragEnter -= TopWindow_DragEnter;
                    this.topWindow.DragOver -= TopWindow_DragOver;
                    this.topWindow.DragLeave -= TopWindow_DragLeave;

                    this.draggedData = null;
                }
            }
        }

        private void DragSource_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            this.draggedData = null;
        }

        // DropTarget

        private void DropTarget_PreviewDragEnter(object sender, DragEventArgs e)
        {
            this.targetItemsControl = (ItemsControl)sender;
            object draggedItem = e.Data.GetData(this.format.Name);

            DecideDropTarget(e);
            if (draggedItem != null)
            {
                // Dragged Adorner is created on the first enter only.
                ShowDraggedAdorner(e.GetPosition(this.topWindow));
                CreateInsertionAdorner();
            }
            e.Handled = true;
        }

        private void DropTarget_PreviewDragOver(object sender, DragEventArgs e)
        {
            object draggedItem = e.Data.GetData(this.format.Name);

            DecideDropTarget(e);
            if (draggedItem != null)
            {
                // Dragged Adorner is only updated here - it has already been created in DragEnter.
                ShowDraggedAdorner(e.GetPosition(this.topWindow));
                UpdateInsertionAdornerPosition();
            }
            e.Handled = true; // Why need e.Handled = true HERE ???
        }

        private void DropTarget_PreviewDrop(object sender, DragEventArgs e)
        {
            object draggedItem = e.Data.GetData(this.format.Name);
            int indexRemoved = -1; bool diff = false;

            if (draggedItem != null)
            {
                if ((e.Effects & DragDropEffects.Move) != 0)
                {   // If Move then remove index, Copy no need remove index
                    indexRemoved = Utilities.RemoveItemFromItemsControl(this.sourceItemsControl, draggedItem);
                }

                if (this.sourceItemsControl == this.targetItemsControl)
                {   // This happens when we drag an item to a later position within the same ItemsControl.
                    if (indexRemoved != -1 && indexRemoved < this.insertionIndex)
                    {
                        this.insertionIndex--;
                    }
                }
                else
                {   // This happen when we drag an item to different control
                    diff = true;
                }

                Utilities.InsertItemInItemsControl(this.targetItemsControl, draggedItem, this.insertionIndex);

                RemoveDraggedAdorner();
                RemoveInsertionAdorner();
            }

            e.Handled = true; // Why need e.Handled = true HERE ???

            if (diff)
            {   // If different control then :
                ICommand command = GetPreviewDropFromDiffHandler(this.targetItemsControl);
                if (command != null) command.Execute(draggedItem); // PreviewDropToDiffHandler Callback (Wrapper by ICommand)

                command = GetPreviewDropToDiffHandler(this.sourceItemsControl);
                if (command != null) command.Execute(draggedItem); // PreviewDropToDiffHandler Callback (Wrapper by ICommand)
            }
        }

        private void DropTarget_PreviewDragLeave(object sender, DragEventArgs e)
        {
            // Dragged Adorner is only created once on DragEnter + every time we enter the window. 
            // It's only removed once on the DragDrop, and every time we leave the window. (so no need to remove it here)
            object draggedItem = e.Data.GetData(this.format.Name);

            if (draggedItem != null)
            {
                RemoveInsertionAdorner();
            }
            e.Handled = true; // Why need e.Handled = true HERE ???
        }

        // If the types of the dragged data and ItemsControl's source are compatible, 
        // there are 3 situations to have into account when deciding the drop target:
        // 1. mouse is over an items container
        // 2. mouse is over the empty part of an ItemsControl, but ItemsControl is not empty
        // 3. mouse is over an empty ItemsControl.
        // The goal of this method is to decide on the values of the following properties: 
        // targetItemContainer, insertionIndex and isInFirstHalf.
        private void DecideDropTarget(DragEventArgs e)
        {
            int targetItemsControlCount = this.targetItemsControl.Items.Count;
            object draggedItem = e.Data.GetData(this.format.Name);

            if (IsDropDataTypeAllowed(draggedItem))
            {
                if (targetItemsControlCount > 0)
                {
                    this.hasVerticalOrientation = Utilities.HasVerticalOrientation(this.targetItemsControl.ItemContainerGenerator.ContainerFromIndex(0) as FrameworkElement);
                    this.targetItemContainer = targetItemsControl.ContainerFromElement((DependencyObject)e.OriginalSource) as FrameworkElement;

                    if (this.targetItemContainer != null)
                    {
                        Point positionRelativeToItemContainer = e.GetPosition(this.targetItemContainer);
                        this.isInFirstHalf = Utilities.IsInFirstHalf(this.targetItemContainer, positionRelativeToItemContainer, this.hasVerticalOrientation);
                        this.insertionIndex = this.targetItemsControl.ItemContainerGenerator.IndexFromContainer(this.targetItemContainer);

                        if (!this.isInFirstHalf)
                        {
                            this.insertionIndex++;
                        }
                    }
                    else
                    {
                        this.targetItemContainer = this.targetItemsControl.ItemContainerGenerator.ContainerFromIndex(targetItemsControlCount - 1) as FrameworkElement;
                        this.isInFirstHalf = false;
                        this.insertionIndex = targetItemsControlCount;
                    }
                }
                else
                {
                    this.targetItemContainer = null;
                    this.insertionIndex = 0;
                }
            }
            else
            {
                this.targetItemContainer = null;
                this.insertionIndex = -1;
                e.Effects = DragDropEffects.None;
            }
        }

        // Can the dragged data be added to the destination collection?
        // It can if destination is bound to IList<allowed type>, IList or not data bound.
        private bool IsDropDataTypeAllowed(object draggedItem)
        {
            bool isDropDataTypeAllowed;
            IEnumerable collectionSource = this.targetItemsControl.ItemsSource;
            if (draggedItem != null)
            {
                if (collectionSource != null)
                {
                    Type draggedType = draggedItem.GetType();
                    Type collectionType = collectionSource.GetType();

                    Type genericIListType = collectionType.GetInterface("IList`1");
                    if (genericIListType != null)
                    {
                        Type[] genericArguments = genericIListType.GetGenericArguments();
                        isDropDataTypeAllowed = genericArguments[0].IsAssignableFrom(draggedType);
                    }
                    else if (typeof(IList).IsAssignableFrom(collectionType))
                    {
                        isDropDataTypeAllowed = true;
                    }
                    else
                    {
                        isDropDataTypeAllowed = false;
                    }
                }
                else // the ItemsControl's ItemsSource is not data bound.
                {
                    isDropDataTypeAllowed = true;
                }
            }
            else
            {
                isDropDataTypeAllowed = false;
            }
            return isDropDataTypeAllowed;
        }

        // Window

        private void TopWindow_DragEnter(object sender, DragEventArgs e)
        {
            ShowDraggedAdorner(e.GetPosition(this.topWindow));
            e.Effects = DragDropEffects.None;
            e.Handled = true;
        }

        private void TopWindow_DragOver(object sender, DragEventArgs e)
        {
            ShowDraggedAdorner(e.GetPosition(this.topWindow));
            e.Effects = DragDropEffects.None;
            e.Handled = true;
        }

        private void TopWindow_DragLeave(object sender, DragEventArgs e)
        {
            RemoveDraggedAdorner();
            e.Handled = true;
        }

        // Adorners

        // Creates or updates the dragged Adorner. 
        private void ShowDraggedAdorner(Point currentPosition)
        {
            if (this.draggedAdorner == null)
            {
                var adornerLayer = AdornerLayer.GetAdornerLayer(this.sourceItemsControl);
                this.draggedAdorner = new DraggedAdorner(this.draggedData, GetDragDropTemplate(this.sourceItemsControl), this.sourceItemContainer, adornerLayer);
            }
            this.draggedAdorner.SetPosition(currentPosition.X - this.initialMousePosition.X + this.initialMouseOffset.X, currentPosition.Y - this.initialMousePosition.Y + this.initialMouseOffset.Y);
        }

        private void RemoveDraggedAdorner()
        {
            if (this.draggedAdorner != null)
            {
                this.draggedAdorner.Detach();
                this.draggedAdorner = null;
            }
        }

        private void CreateInsertionAdorner()
        {
            if (this.targetItemContainer != null)
            {
                // Here, I need to get adorner layer from targetItemContainer and not targetItemsControl. 
                // This way I get the AdornerLayer within ScrollContentPresenter, and not the one under AdornerDecorator (Snoop is awesome).
                // If I used targetItemsControl, the adorner would hang out of ItemsControl when there's a horizontal scroll bar.
                var adornerLayer = AdornerLayer.GetAdornerLayer(this.targetItemContainer);
                this.insertionAdorner = new InsertionAdorner(this.hasVerticalOrientation, this.isInFirstHalf, this.targetItemContainer, adornerLayer);
            }
        }

        private void UpdateInsertionAdornerPosition()
        {
            if (this.insertionAdorner != null)
            {
                this.insertionAdorner.IsInFirstHalf = this.isInFirstHalf;
                this.insertionAdorner.InvalidateVisual();
            }
        }

        private void RemoveInsertionAdorner()
        {
            if (this.insertionAdorner != null)
            {
                this.insertionAdorner.Detach();
                this.insertionAdorner = null;
            }
        }
    }

    public class DoRemoveItemArgs
    {
        public object Item { get; private set; }

        /// <summary>
        /// RemoveItemArgument for DragDropHelper.DoRemoveItem
        /// </summary>
        /// <param name="item"></param>
        public DoRemoveItemArgs(object item)
        {
            Item = item;
        }
    }

    public class DoDragDropTargetEventArgs
    {
        public ItemsControl Target { get; private set; }
        public object Item { get; private set; }
        public int Index { get; private set; }

        /// <summary>
        /// DragDropArgument for DragDropHelper.DoDragDrop
        /// </summary>
        /// <param name="target"></param>
        /// <param name="item"></param>
        /// <param name="indexInsert">-1 will add to the end</param>
        public DoDragDropTargetEventArgs(ItemsControl target, object item, int indexInsert = -1)
        {
            Target = target;
            Item = item;
            Index = indexInsert;
        }
    }

    public class DropCommand : ICommand
    {
        Action<object> _executeDelegate;

        /// <summary>
        /// Create a new Drop Command Wrapper for Delegate Callback in DragDropHelper
        /// </summary>
        /// <param name="executeDelegate">Delegate with object = draggedItem. You can then cast draggedItem to your using Item Type. Eg : Tool draggedTool = draggedItem as Tool</param>
        public DropCommand(Action<object> executeDelegate)
        {
            _executeDelegate = executeDelegate;
        }

        // Summary:
        //     Occurs when changes occur that affect whether or not the command should execute.
        public event EventHandler CanExecuteChanged;

        // Summary:
        //     Defines the method that determines whether the command can execute in its
        //     current state.
        //
        // Parameters:
        //   parameter:
        //     Data used by the command. If the command does not require data to be passed,
        //     this object can be set to null.
        //
        // Returns:
        //     true if this command can be executed; otherwise, false.
        public bool CanExecute(object parameter)
        {
            return true;
        }
        //
        // Summary:
        //     Defines the method to be called when the command is invoked.
        //
        // Parameters:
        //   parameter:
        //     Data used by the command. If the command does not require data to be passed,
        //     this object can be set to null.
        public void Execute(object parameter)
        {
            _executeDelegate(parameter);
        }
    }

    public class RelayCommand<T> : ICommand
    {
        #region Fields

        readonly Action<T> _execute = null;
        readonly Predicate<T> _canExecute = null;

        #endregion // Fields

        #region Constructors

        public RelayCommand(Action<T> execute)
            : this(execute, null)
        {
        }

        /// <summary>
        /// Creates a new command.
        /// </summary>
        /// <param name="execute">The execution logic.</param>
        /// <param name="canExecute">The execution status logic.</param>
        public RelayCommand(Action<T> execute, Predicate<T> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }

        #endregion // Constructors

        #region ICommand Members

        [System.Diagnostics.DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute((T)parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            _execute((T)parameter);
        }

        #endregion // ICommand Members
    }

    // http://snipplr.com/view/13642/
    public class RelayCommand : ICommand
    {
        #region Fields

        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;

        #endregion // Fields

        #region Constructors

        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }
        #endregion // Constructors

        #region ICommand Members

        [System.Diagnostics.DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

        #endregion // ICommand Members
    }
}

























#if false
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Controls;
using System.Collections;
using System.Windows.Documents;
using System.Reflection;

namespace FarpacHelper
{
    public class DragDropHelper
    {
        // source and target
        private DataFormat format = DataFormats.GetDataFormat("DragDropItemsControl");
        private Point initialMousePosition;
        private Vector initialMouseOffset;
        private object draggedData;
        private DraggedAdorner draggedAdorner;
        private InsertionAdorner insertionAdorner;
        private Window topWindow;
        // source
        private ItemsControl sourceItemsControl;
        private FrameworkElement sourceItemContainer;
        // target
        private ItemsControl targetItemsControl;
        private FrameworkElement targetItemContainer;
        private bool hasVerticalOrientation;
        private int insertionIndex;
        private bool isInFirstHalf;
        // singleton
        private static DragDropHelper instance;
        private static DragDropHelper Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new DragDropHelper();
                }
                return instance;
            }
        }

#region IsDragSourceProperty
        public static readonly DependencyProperty IsDragSourceProperty =
            DependencyProperty.RegisterAttached("IsDragSource", typeof(bool), typeof(DragDropHelper), new UIPropertyMetadata(false, IsDragSourceChanged));

        public static bool GetIsDragSource(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsDragSourceProperty);
        }

        public static void SetIsDragSource(DependencyObject obj, bool value)
        {
            obj.SetValue(IsDragSourceProperty, value);
        }

        private static void IsDragSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var dragSource = obj as ItemsControl;
            if (dragSource != null)
            {
                if (Object.Equals(e.NewValue, true))
                {
                    dragSource.PreviewMouseLeftButtonDown += Instance.DragSource_PreviewMouseLeftButtonDown;
                    dragSource.PreviewMouseLeftButtonUp += Instance.DragSource_PreviewMouseLeftButtonUp;
                    dragSource.PreviewMouseMove += Instance.DragSource_PreviewMouseMove;
                }
                else
                {
                    dragSource.PreviewMouseLeftButtonDown -= Instance.DragSource_PreviewMouseLeftButtonDown;
                    dragSource.PreviewMouseLeftButtonUp -= Instance.DragSource_PreviewMouseLeftButtonUp;
                    dragSource.PreviewMouseMove -= Instance.DragSource_PreviewMouseMove;
                }
            }
        }
#endregion

#region IsDropTargetProperty
        public static readonly DependencyProperty IsDropTargetProperty =
            DependencyProperty.RegisterAttached("IsDropTarget", typeof(bool), typeof(DragDropHelper), new UIPropertyMetadata(false, IsDropTargetChanged));

        public static bool GetIsDropTarget(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsDropTargetProperty);
        }

        public static void SetIsDropTarget(DependencyObject obj, bool value)
        {
            obj.SetValue(IsDropTargetProperty, value);
        }

        private static void IsDropTargetChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var dropTarget = obj as ItemsControl;
            if (dropTarget != null)
            {
                if (Object.Equals(e.NewValue, true))
                {
                    dropTarget.AllowDrop = true;
                    dropTarget.PreviewDrop += Instance.DropTarget_PreviewDrop;
                    dropTarget.PreviewDragEnter += Instance.DropTarget_PreviewDragEnter;
                    dropTarget.PreviewDragOver += Instance.DropTarget_PreviewDragOver;
                    dropTarget.PreviewDragLeave += Instance.DropTarget_PreviewDragLeave;
                }
                else
                {
                    dropTarget.AllowDrop = false;
                    dropTarget.PreviewDrop -= Instance.DropTarget_PreviewDrop;
                    dropTarget.PreviewDragEnter -= Instance.DropTarget_PreviewDragEnter;
                    dropTarget.PreviewDragOver -= Instance.DropTarget_PreviewDragOver;
                    dropTarget.PreviewDragLeave -= Instance.DropTarget_PreviewDragLeave;
                }
            }
        }
#endregion

#region DragDropTemplateProperty
        public static DataTemplate GetDragDropTemplate(DependencyObject obj)
        {
            return (DataTemplate)obj.GetValue(DragDropTemplateProperty);
        }

        public static void SetDragDropTemplate(DependencyObject obj, DataTemplate value)
        {
            obj.SetValue(DragDropTemplateProperty, value);
        }

        public static readonly DependencyProperty DragDropTemplateProperty =
            DependencyProperty.RegisterAttached("DragDropTemplate", typeof(DataTemplate), typeof(DragDropHelper), new UIPropertyMetadata(null));
#endregion

#region IsCopyProperty
        public static readonly DependencyProperty IsCopyProperty =
            DependencyProperty.RegisterAttached("IsCopy", typeof(bool), typeof(DragDropHelper), new UIPropertyMetadata(false));

        public static bool GetIsCopy(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsCopyProperty);
        }

        public static void SetIsCopy(DependencyObject obj, bool value)
        {
            obj.SetValue(IsCopyProperty, value);
        }
#endregion

#region DoDragDropProperty
        public static readonly DependencyProperty DoDragDropProperty =
            DependencyProperty.RegisterAttached("DoDragDrop", typeof(DoDragDropTargetEventArgs), typeof(DragDropHelper), new UIPropertyMetadata(null, DoDragDropChanged));

        public static DoDragDropTargetEventArgs GetDoDragDrop(DependencyObject obj)
        {
            return (DoDragDropTargetEventArgs)obj.GetValue(DoDragDropProperty);
        }

        public static void SetDoDragDrop(DependencyObject obj, DoDragDropTargetEventArgs value)
        {
            obj.SetValue(DoDragDropProperty, value);
        }

        private static void DoDragDropChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var TargetArg = e.NewValue as DoDragDropTargetEventArgs;
            if (TargetArg != null)
            {   // If change to not NULL execute METHOD
                var dragSource = obj as ItemsControl;
                var dropTarget = TargetArg.Target;
                object draggedItem = TargetArg.Item;
                int insertIndex = TargetArg.Index;
                if (insertIndex == -1) insertIndex = dropTarget.Items.Count; // If -1 then add to end of items list.
                // MORE CODE HERE

                // If Copy no need to Removed
                int indexRemoved = -1; bool diff = false;

                if (draggedItem != null)
                {
                    if (GetIsCopy(dragSource) == false) indexRemoved = Utilities.RemoveItemFromItemsControl(dragSource, draggedItem);

                    if (dragSource == dropTarget)
                    {   // This happens when we drag an item to a later position within the same ItemsControl.
                        if (indexRemoved != -1 && indexRemoved < insertIndex)
                        {
                            insertIndex--;
                        }
                    }
                    else
                    {   // This happen when we drag an item to different control
                        diff = true;
                    }

                    Utilities.InsertItemInItemsControl(dropTarget, draggedItem, insertIndex);

                    //Instance.RemoveDraggedAdorner();
                    //Instance.RemoveInsertionAdorner();
                }


                if (diff)
                {   // If different control then :
                    ICommand command = GetPreviewDropFromDiffHandler(dropTarget);
                    if (command != null) command.Execute(draggedItem); // PreviewDropToDiffHandler Callback (Wrapper by ICommand)

                    command = GetPreviewDropToDiffHandler(dragSource);
                    if (command != null) command.Execute(draggedItem); // PreviewDropToDiffHandler Callback (Wrapper by ICommand)
                }

            }
            // Code to reset NewValue to avoid not call DoDragDropChanged if same DoDragDropTargetEventArgs pass into HERE
        }
#endregion

#region DoRemoveItemProperty
        public static readonly DependencyProperty DoRemoveItemProperty =
            DependencyProperty.RegisterAttached("DoRemoveItem", typeof(DoRemoveItemArgs), typeof(DragDropHelper), new UIPropertyMetadata(null, DoRemoveItemChanged));

        public static DoRemoveItemArgs GetDoRemoveItem(DependencyObject obj)
        {
            return (DoRemoveItemArgs)obj.GetValue(DoRemoveItemProperty);
        }

        public static void SetDoRemoveItem(DependencyObject obj, DoRemoveItemArgs value)
        {
            obj.SetValue(DoRemoveItemProperty, value);
        }

        private static void DoRemoveItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var removeItem = (e.NewValue as DoRemoveItemArgs).Item;
            if (removeItem != null)
            {   // If change to not NULL execute METHOD
                var removeSource = obj as ItemsControl;
                Utilities.RemoveItemFromItemsControl(removeSource, removeItem);

                // Different control but don't have dropTarget then :
                ICommand command = GetPreviewDropToDiffHandler(removeSource);
                if (command != null) command.Execute(removeItem); // PreviewDropToDiffHandler Callback (Wrapper by ICommand)
            }
            // Code to reset NewValue to avoid not call DoDragDropChanged if same DoDragDropTargetEventArgs pass into HERE
        }
#endregion

#region PreviewDropFromDiffCommand
        /// <summary>
        /// Get the PreviewDropHandler for this Target Object (own this Property) from Different Source obj. If obj = null then no need to Call this
        /// </summary>
        /// <param name="obj">ItemsControl Source that Different from this Target Object (own this Property)</param>
        /// <returns></returns>
        public static ICommand GetPreviewDropFromDiffHandler(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(PreviewDropFromDiffHandlerProperty);
        }

        public static void SetPreviewDropFromDiffHandler(DependencyObject obj, ICommand value)
        {
            obj.SetValue(PreviewDropFromDiffHandlerProperty, value);
        }

        public static readonly DependencyProperty PreviewDropFromDiffHandlerProperty =
            DependencyProperty.RegisterAttached("PreviewDropFromDiffHandler", typeof(ICommand), typeof(DragDropHelper), new UIPropertyMetadata(null));
#endregion

#region PreviewDropToDiffCommand
        public static ICommand GetPreviewDropToDiffHandler(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(PreviewDropToDiffHandlerProperty);
        }

        public static void SetPreviewDropToDiffHandler(DependencyObject obj, ICommand value)
        {
            obj.SetValue(PreviewDropToDiffHandlerProperty, value);
        }

        public static readonly DependencyProperty PreviewDropToDiffHandlerProperty =
            DependencyProperty.RegisterAttached("PreviewDropToDiffHandler", typeof(ICommand), typeof(DragDropHelper), new UIPropertyMetadata(null));
#endregion

        // DragSource

        private void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.sourceItemsControl = (ItemsControl)sender;
            Visual visual = e.OriginalSource as Visual;

            this.topWindow = Window.GetWindow(this.sourceItemsControl);
            this.initialMousePosition = e.GetPosition(this.topWindow);

            this.sourceItemContainer = sourceItemsControl.ContainerFromElement(visual) as FrameworkElement;
            if (this.sourceItemContainer != null)
            {
                this.draggedData = this.sourceItemContainer.DataContext;
            }
        }

        // Drag = mouse down + move by a certain amount
        private void DragSource_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (this.draggedData != null)
            {
                // Only drag when user moved the mouse by a reasonable amount.
                if (Utilities.IsMovementBigEnough(this.initialMousePosition, e.GetPosition(this.topWindow)))
                {
                    this.initialMouseOffset = this.initialMousePosition - this.sourceItemContainer.TranslatePoint(new Point(0, 0), this.topWindow);

                    DataObject data = new DataObject(this.format.Name, this.draggedData);

                    // Adding events to the window to make sure dragged adorner comes up when mouse is not over a drop target.
                    bool previousAllowDrop = this.topWindow.AllowDrop;
                    this.topWindow.AllowDrop = true;
                    this.topWindow.DragEnter += TopWindow_DragEnter;
                    this.topWindow.DragOver += TopWindow_DragOver;
                    this.topWindow.DragLeave += TopWindow_DragLeave;

                    // If source isCopy then
                    //      If have target 
                    //          If same source Move
                    //          If diff source Copy
                    //      If no target
                    //          Copy
                    // Else Move

                    // If source isCopy & have target & diff target then Copy
                    // Else Move

                    if (GetIsCopy(this.sourceItemsControl)) DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Copy);
                    else DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move);

                    //DragDropEffects effects = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Copy);
                    /*if (this.targetItemsControl != null)
                    {   // If Drag into Same Control => Always Move
                        // Else If Diff. Control but Source Allow Copy then Copy 
                        // Else Move
                        if (this.sourceItemsControl == this.targetItemsControl) DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move);
                        else
                            if (GetIsCopy(this.sourceItemsControl)) DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Copy);
                            else DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move);
                    }
                    else
                    {   // If Drag into Nowhere = Copy => Nothing to do !
                        if (GetIsCopy(this.sourceItemsControl)) DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Copy);
                        else DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move);
                    }*/

                    // Without this call, there would be a bug in the following scenario: Click on a data item, and drag
                    // the mouse very fast outside of the window. When doing this really fast, for some reason I don't get 
                    // the Window leave event, and the dragged adorner is left behind.
                    // With this call, the dragged adorner will disappear when we release the mouse outside of the window,
                    // which is when the DoDragDrop synchronous method returns.
                    RemoveDraggedAdorner();

                    this.topWindow.AllowDrop = previousAllowDrop;
                    this.topWindow.DragEnter -= TopWindow_DragEnter;
                    this.topWindow.DragOver -= TopWindow_DragOver;
                    this.topWindow.DragLeave -= TopWindow_DragLeave;

                    this.draggedData = null;
                }
            }
        }

        private void DragSource_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            this.draggedData = null;
        }

        // DropTarget

        private void DropTarget_PreviewDragEnter(object sender, DragEventArgs e)
        {
            this.targetItemsControl = (ItemsControl)sender;
            object draggedItem = e.Data.GetData(this.format.Name);

            DecideDropTarget(e);
            if (draggedItem != null)
            {
                // Dragged Adorner is created on the first enter only.
                ShowDraggedAdorner(e.GetPosition(this.topWindow));
                CreateInsertionAdorner();
            }
            e.Handled = true;
        }

        private void DropTarget_PreviewDragOver(object sender, DragEventArgs e)
        {
            object draggedItem = e.Data.GetData(this.format.Name);

            DecideDropTarget(e);
            if (draggedItem != null)
            {
                // Dragged Adorner is only updated here - it has already been created in DragEnter.
                ShowDraggedAdorner(e.GetPosition(this.topWindow));
                UpdateInsertionAdornerPosition();
            }
            e.Handled = true; // Why need e.Handled = true HERE ???
        }

        private void DropTarget_PreviewDrop(object sender, DragEventArgs e)
        {
            object draggedItem = e.Data.GetData(this.format.Name);
            int indexRemoved = -1; bool diff = false;

            if (draggedItem != null)
            {
                if ((e.Effects & DragDropEffects.Move) != 0)
                {   // If Move then remove index, Copy no need remove index
                    indexRemoved = Utilities.RemoveItemFromItemsControl(this.sourceItemsControl, draggedItem);
                }

                if (this.sourceItemsControl == this.targetItemsControl)
                {   // This happens when we drag an item to a later position within the same ItemsControl.
                    if (indexRemoved != -1 && indexRemoved < this.insertionIndex)
                    {
                        this.insertionIndex--;
                    }
                }
                else
                {   // This happen when we drag an item to different control
                    diff = true;
                }

                Utilities.InsertItemInItemsControl(this.targetItemsControl, draggedItem, this.insertionIndex);

                RemoveDraggedAdorner();
                RemoveInsertionAdorner();
            }

            e.Handled = true; // Why need e.Handled = true HERE ???

            if (diff)
            {   // If different control then :
                ICommand command = GetPreviewDropFromDiffHandler(this.targetItemsControl);
                if (command != null) command.Execute(draggedItem); // PreviewDropToDiffHandler Callback (Wrapper by ICommand)

                command = GetPreviewDropToDiffHandler(this.sourceItemsControl);
                if (command != null) command.Execute(draggedItem); // PreviewDropToDiffHandler Callback (Wrapper by ICommand)
            }
        }

        private void DropTarget_PreviewDragLeave(object sender, DragEventArgs e)
        {
            // Dragged Adorner is only created once on DragEnter + every time we enter the window. 
            // It's only removed once on the DragDrop, and every time we leave the window. (so no need to remove it here)
            object draggedItem = e.Data.GetData(this.format.Name);

            if (draggedItem != null)
            {
                RemoveInsertionAdorner();
            }
            e.Handled = true; // Why need e.Handled = true HERE ???
        }

        // If the types of the dragged data and ItemsControl's source are compatible, 
        // there are 3 situations to have into account when deciding the drop target:
        // 1. mouse is over an items container
        // 2. mouse is over the empty part of an ItemsControl, but ItemsControl is not empty
        // 3. mouse is over an empty ItemsControl.
        // The goal of this method is to decide on the values of the following properties: 
        // targetItemContainer, insertionIndex and isInFirstHalf.
        private void DecideDropTarget(DragEventArgs e)
        {
            int targetItemsControlCount = this.targetItemsControl.Items.Count;
            object draggedItem = e.Data.GetData(this.format.Name);

            if (IsDropDataTypeAllowed(draggedItem))
            {
                if (targetItemsControlCount > 0)
                {
                    this.hasVerticalOrientation = Utilities.HasVerticalOrientation(this.targetItemsControl.ItemContainerGenerator.ContainerFromIndex(0) as FrameworkElement);
                    this.targetItemContainer = targetItemsControl.ContainerFromElement((DependencyObject)e.OriginalSource) as FrameworkElement;

                    if (this.targetItemContainer != null)
                    {
                        Point positionRelativeToItemContainer = e.GetPosition(this.targetItemContainer);
                        this.isInFirstHalf = Utilities.IsInFirstHalf(this.targetItemContainer, positionRelativeToItemContainer, this.hasVerticalOrientation);
                        this.insertionIndex = this.targetItemsControl.ItemContainerGenerator.IndexFromContainer(this.targetItemContainer);

                        if (!this.isInFirstHalf)
                        {
                            this.insertionIndex++;
                        }
                    }
                    else
                    {
                        this.targetItemContainer = this.targetItemsControl.ItemContainerGenerator.ContainerFromIndex(targetItemsControlCount - 1) as FrameworkElement;
                        this.isInFirstHalf = false;
                        this.insertionIndex = targetItemsControlCount;
                    }
                }
                else
                {
                    this.targetItemContainer = null;
                    this.insertionIndex = 0;
                }
            }
            else
            {
                this.targetItemContainer = null;
                this.insertionIndex = -1;
                e.Effects = DragDropEffects.None;
            }
        }

        // Can the dragged data be added to the destination collection?
        // It can if destination is bound to IList<allowed type>, IList or not data bound.
        private bool IsDropDataTypeAllowed(object draggedItem)
        {
            bool isDropDataTypeAllowed;
            IEnumerable collectionSource = this.targetItemsControl.ItemsSource;
            if (draggedItem != null)
            {
                if (collectionSource != null)
                {
                    Type draggedType = draggedItem.GetType();
                    Type collectionType = collectionSource.GetType();

                    Type genericIListType = collectionType.GetInterface("IList`1");
                    if (genericIListType != null)
                    {
                        Type[] genericArguments = genericIListType.GetGenericArguments();
                        isDropDataTypeAllowed = genericArguments[0].IsAssignableFrom(draggedType);
                    }
                    else if (typeof(IList).IsAssignableFrom(collectionType))
                    {
                        isDropDataTypeAllowed = true;
                    }
                    else
                    {
                        isDropDataTypeAllowed = false;
                    }
                }
                else // the ItemsControl's ItemsSource is not data bound.
                {
                    isDropDataTypeAllowed = true;
                }
            }
            else
            {
                isDropDataTypeAllowed = false;
            }
            return isDropDataTypeAllowed;
        }

        // Window

        private void TopWindow_DragEnter(object sender, DragEventArgs e)
        {
            ShowDraggedAdorner(e.GetPosition(this.topWindow));
            e.Effects = DragDropEffects.None;
            e.Handled = true;
        }

        private void TopWindow_DragOver(object sender, DragEventArgs e)
        {
            ShowDraggedAdorner(e.GetPosition(this.topWindow));
            e.Effects = DragDropEffects.None;
            e.Handled = true;
        }

        private void TopWindow_DragLeave(object sender, DragEventArgs e)
        {
            RemoveDraggedAdorner();
            e.Handled = true;
        }

        // Adorners

        // Creates or updates the dragged Adorner. 
        private void ShowDraggedAdorner(Point currentPosition)
        {
            if (this.draggedAdorner == null)
            {
                var adornerLayer = AdornerLayer.GetAdornerLayer(this.sourceItemsControl);
                this.draggedAdorner = new DraggedAdorner(this.draggedData, GetDragDropTemplate(this.sourceItemsControl), this.sourceItemContainer, adornerLayer);
            }
            this.draggedAdorner.SetPosition(currentPosition.X - this.initialMousePosition.X + this.initialMouseOffset.X, currentPosition.Y - this.initialMousePosition.Y + this.initialMouseOffset.Y);
        }

        private void RemoveDraggedAdorner()
        {
            if (this.draggedAdorner != null)
            {
                this.draggedAdorner.Detach();
                this.draggedAdorner = null;
            }
        }

        private void CreateInsertionAdorner()
        {
            if (this.targetItemContainer != null)
            {
                // Here, I need to get adorner layer from targetItemContainer and not targetItemsControl. 
                // This way I get the AdornerLayer within ScrollContentPresenter, and not the one under AdornerDecorator (Snoop is awesome).
                // If I used targetItemsControl, the adorner would hang out of ItemsControl when there's a horizontal scroll bar.
                var adornerLayer = AdornerLayer.GetAdornerLayer(this.targetItemContainer);
                this.insertionAdorner = new InsertionAdorner(this.hasVerticalOrientation, this.isInFirstHalf, this.targetItemContainer, adornerLayer);
            }
        }

        private void UpdateInsertionAdornerPosition()
        {
            if (this.insertionAdorner != null)
            {
                this.insertionAdorner.IsInFirstHalf = this.isInFirstHalf;
                this.insertionAdorner.InvalidateVisual();
            }
        }

        private void RemoveInsertionAdorner()
        {
            if (this.insertionAdorner != null)
            {
                this.insertionAdorner.Detach();
                this.insertionAdorner = null;
            }
        }
    }

    public class DoRemoveItemArgs
    {
        public object Item { get; private set; }

        /// <summary>
        /// RemoveItemArgument for DragDropHelper.DoRemoveItem
        /// </summary>
        /// <param name="item"></param>
        public DoRemoveItemArgs(object item)
        {
            Item = item;
        }
    }

    public class DoDragDropTargetEventArgs
    {
        public ItemsControl Target { get; private set; }
        public object Item { get; private set; }
        public int Index { get; private set; }

        /// <summary>
        /// DragDropArgument for DragDropHelper.DoDragDrop
        /// </summary>
        /// <param name="target"></param>
        /// <param name="item"></param>
        /// <param name="indexInsert">-1 will add to the end</param>
        public DoDragDropTargetEventArgs(ItemsControl target, object item, int indexInsert = -1)
        {
            Target = target;
            Item = item;
            Index = indexInsert;
        }
    }

    public class DropCommand : ICommand
    {
        Action<object> _executeDelegate;

        /// <summary>
        /// Create a new Drop Command Wrapper for Delegate Callback in DragDropHelper
        /// </summary>
        /// <param name="executeDelegate">Delegate with object = draggedItem. You can then cast draggedItem to your using Item Type. Eg : Tool draggedTool = draggedItem as Tool</param>
        public DropCommand(Action<object> executeDelegate)
        {
            _executeDelegate = executeDelegate;
        }

        // Summary:
        //     Occurs when changes occur that affect whether or not the command should execute.
        public event EventHandler CanExecuteChanged;

        // Summary:
        //     Defines the method that determines whether the command can execute in its
        //     current state.
        //
        // Parameters:
        //   parameter:
        //     Data used by the command. If the command does not require data to be passed,
        //     this object can be set to null.
        //
        // Returns:
        //     true if this command can be executed; otherwise, false.
        public bool CanExecute(object parameter)
        {
            return true;
        }
        //
        // Summary:
        //     Defines the method to be called when the command is invoked.
        //
        // Parameters:
        //   parameter:
        //     Data used by the command. If the command does not require data to be passed,
        //     this object can be set to null.
        public void Execute(object parameter)
        {
            _executeDelegate(parameter);
        }
    }

    public class RelayCommand<T> : ICommand
    {
#region Fields

        readonly Action<T> _execute = null;
        readonly Predicate<T> _canExecute = null;

#endregion // Fields

#region Constructors

        public RelayCommand(Action<T> execute)
            : this(execute, null)
        {
        }

        /// <summary>
        /// Creates a new command.
        /// </summary>
        /// <param name="execute">The execution logic.</param>
        /// <param name="canExecute">The execution status logic.</param>
        public RelayCommand(Action<T> execute, Predicate<T> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }

#endregion // Constructors

#region ICommand Members

        [System.Diagnostics.DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute((T)parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            _execute((T)parameter);
        }

#endregion // ICommand Members
    }

    // http://snipplr.com/view/13642/
    public class RelayCommand : ICommand
    {
#region Fields

        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;

#endregion // Fields

#region Constructors

        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }
#endregion // Constructors

#region ICommand Members

        [System.Diagnostics.DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

#endregion // ICommand Members
    }
}
#endif